import { isArray, isFunction, isObject } from 'lodash-es'
import { getTsToJs } from '../setupLogic'

import {
  BwCustomComNodeAttrDataTypeEnum,
  BwCustomComNodeAttrFromTypeEnum as BwAttrFromEnum,
  BwCustomComNodeAttrJsExpParamFromTypeEnum as BwJsExpParamFromTypeEnum,
  BwCustomComNodeAttrJsExpParamDataTypeEnum as BwJsExpParamDataTypeEnum,
} from '../../models'

import type { BwVueIns } from '../model'
import type {
  BwCustomComNodeAttrModel,
  BwCustomComNodeIfJsExpParamModel,
  BwCustomComNodeForJsExpParamModel,
  BwCustomComNodeAttrJsExpParamModel,
} from '../../models'

/** 获取属性常量值*/
export function getComAttrConstValue(
  type: BwCustomComNodeAttrDataTypeEnum | null,
  constValue?: string,
  vueIns?: BwVueIns,
  scopeData?: Record<string, any>
) {
  switch (type) {
    case BwCustomComNodeAttrDataTypeEnum.string: {
      return constValue
    }
    case BwCustomComNodeAttrDataTypeEnum.number: {
      return [undefined, null, '', 'null', 'undefined'].includes(constValue)
        ? undefined
        : Number(constValue)
    }
    case BwCustomComNodeAttrDataTypeEnum.boolean: {
      return ['true', true].includes(constValue ?? '')
    }
    case BwCustomComNodeAttrDataTypeEnum.array: {
      let arrayValue: any[] | undefined
      if (isArray(constValue)) {
        arrayValue = constValue
      } else {
        try {
          arrayValue = constValue ? JSON.parse(constValue) : undefined
        } catch (error) {
          console.error(error)
          arrayValue = undefined
        }
      }

      const newConstValue = arrayValue?.map((item) => {
        if (typeof item === 'object' && vueIns) {
          Object.keys(item).forEach((key) => {
            /** 对对象数组内的对象属性进行特殊处理 */
            if (typeof item[key] === 'object' && item[key]?.valueFromType) {
              item[key] = getComAttrValue(vueIns, item[key], scopeData)
            }
          })
        }

        return item
      })

      return newConstValue
    }
    case BwCustomComNodeAttrDataTypeEnum.object: {
      try {
        return constValue ? JSON.parse(constValue) : undefined
      } catch (error) {
        console.error(error)
        return undefined
      }
    }
    default: {
      return undefined
    }
  }
}

/** 获取组件属性值 */
export function getComAttrValue(
  vueIns: BwVueIns,
  data: BwCustomComNodeAttrModel,
  scopeData?: Record<string, any>
) {
  switch (data?.valueFromType) {
    case BwAttrFromEnum.constValue: {
      return getComAttrConstValue(
        data.valueDataType,
        data.constValue,
        vueIns,
        scopeData
      )
    }

    case BwAttrFromEnum.comVar: {
      const fields = (data.comVarName ?? '').split('.')
      if (fields.length === 1) {
        return vueIns[fields[0]]
      } else {
        let curValue: any = undefined
        fields.forEach((field, index) => {
          if (index) {
            isObject(curValue)
              ? // @ts-ignore
                (curValue = curValue[field])
              : (curValue = undefined)
          } else {
            curValue = vueIns[field]
          }
        })
        return curValue
      }
    }

    case BwAttrFromEnum.comProps: {
      const curProps = vueIns.$props
      return curProps[data.comPropsName ?? '']
    }

    case BwAttrFromEnum.JSExpression: {
      let curJsLogic = data.jsExpBodyLogic ?? ''
      curJsLogic = getTsToJs(curJsLogic)
      curJsLogic = curJsLogic.replace('export default', 'return ')
      if (curJsLogic) {
        const fn = new Function('bwUtils', curJsLogic)

        const extndJSs = fn(vueIns)
        if (isFunction(extndJSs?.extnd)) {
          const params = getJSExpParams(
            vueIns,
            scopeData ?? {},
            data.jsExpParams ?? []
          )

          return extndJSs.extnd(params)
        }
      }
      return undefined
    }
  }
}

/** 获取 Js 表达式参数集合 */
export function getJSExpParams(
  vueIns: BwVueIns,
  scopeData: Record<string, any>,
  data: Array<
    | BwCustomComNodeIfJsExpParamModel
    | BwCustomComNodeForJsExpParamModel
    | BwCustomComNodeAttrJsExpParamModel
  >
) {
  const params: Record<string, any> = {}
  if (Array.isArray(data) && data.length) {
    data.forEach((item) => {
      const name = item.key
      if (name) {
        switch (item.paramFromType) {
          case BwJsExpParamFromTypeEnum.constValue: {
            params[name] = getJSExpParamConstValue(item.paramDataType)
            break
          }
          case BwJsExpParamFromTypeEnum.comVar: {
            const fields = (item.comVarName ?? '').split('.')
            if (fields.length === 1) {
              params[name] = vueIns[fields[0]]
            } else {
              let curValue: any = undefined
              fields.forEach((field, index) => {
                if (index) {
                  isObject(curValue)
                    ? // @ts-ignore
                      (curValue = curValue[field])
                    : (curValue = undefined)
                } else {
                  curValue = vueIns[field]
                }
              })
              params[name] = curValue
            }
            break
          }

          case BwJsExpParamFromTypeEnum.comProps: {
            const curProps = vueIns.$props
            params[name] = curProps[item.comPropsName ?? '']
            break
          }

          case BwJsExpParamFromTypeEnum.forVar: {
            const forVar = item.nodeForVarName ?? ''
            if (forVar) {
              const forData = scopeData.forData
              const [nodeId, field] = forVar.split('&')

              params[name] = (forData?.[nodeId] ?? {})[field]
            }

            break
          }

          case BwJsExpParamFromTypeEnum.slotVar: {
            params[name] = scopeData
            break
          }
        }
      }
    })
  }
  return params
}

/** 获取 Js 表达式参数常量值*/
export function getJSExpParamConstValue(
  type: BwJsExpParamDataTypeEnum,
  constValue?: string
) {
  switch (type) {
    case BwJsExpParamDataTypeEnum.string: {
      return constValue
    }
    case BwJsExpParamDataTypeEnum.number: {
      return [undefined, null, '', 'null', 'undefined'].includes(constValue)
        ? undefined
        : Number(constValue)
    }
    case BwJsExpParamDataTypeEnum.boolean: {
      return ['true', true].includes(constValue ?? '')
    }
    case BwJsExpParamDataTypeEnum.array:
    case BwJsExpParamDataTypeEnum.object: {
      try {
        return constValue ? JSON.parse(constValue) : undefined
      } catch (error) {
        console.error(error)
        return undefined
      }
    }
    default: {
      return undefined
    }
  }
}
